#include "CreateCommand.h"

const string CreateCommand::MESSAGE_NO_USER_INPUT_AFTER_COMMAND = "No user input after create command. Please enter again";
const string CreateCommand::MESSAGE_INVALID_INPUT_FORMAT = "Invalid input format. Please enter again";
const string CreateCommand::MESSAGE_CREATE_UNDO_SUCCESSFULLY = "Undo successful: Created task has been deleted.";
const string CreateCommand::MESSAGE_INVALID_TIME = "Invalid time entered. Please enter again";
const string CreateCommand::MESSAGE_INVALID_DATE = "Invalid date entered. Please enter again" ;
const string CreateCommand::MESSAGE_INVALID_TIME_2 = "There is a problem with time. Try Again!";
const string CreateCommand::MESSAGE_INVALID_INPUT_AFTER_KEY = "No valid input after key. Please enter again";
const string CreateCommand::MESSAGE_INVALID_TIME_FORMAT = "Invalid time format. Try Again!";
const string CreateCommand::MESSAGE_INVALID_PRIORITY = "Invalid priority entered. Try Again!";
const string CreateCommand::MESSAGE_KEY_REPETITION = "There is a repetition of key. Please Check";
const string CreateCommand::MESSAGE_KEYWORD_INVALID = "The keyword entered does not have space around it. Please try gain!";
const string CreateCommand::MESSAGE_INVALID_STARTTIME_BEFORE_ENDTIME = "The end time is before star time. Please try again";
const string CreateCommand::MESSAGE_TWO_DATES_ENTERED = "Two dates are entered. Try Again!";


CreateCommand::CreateCommand(string inputSentence)
{
	storage=Storage::getInstance();
	restOfCommand = inputSentence;
	time(&currentTime);
	parser(restOfCommand);
	index=storage->size();
	task->setIndex(index);
}

CreateCommand::~CreateCommand(void)
{
}

string CreateCommand::Execute()
{
	return storage->addTask(task);
}

string CreateCommand::Unexecute()
{
	storage->deleteTask(index);
	return MESSAGE_CREATE_UNDO_SUCCESSFULLY;
}

////@author A0100248W

// This is the main parser that takes in the a string and calls methods to analyze the string
void CreateCommand::parser(string inputSentence)
{
	if(inputSentence.empty()){
		throw MESSAGE_NO_USER_INPUT_AFTER_COMMAND;
	}
	inputSentence.erase(inputSentence.begin(),inputSentence.begin()+1); // checking for space.
	vector<string> taskInputSentence;
	bool dateChecker = false;
	string buf; 
	stringstream ss(inputSentence); 

	while (ss >> buf)
		taskInputSentence.push_back(buf);
	if(buf.empty()){
		throw MESSAGE_NO_USER_INPUT_AFTER_COMMAND;
	}

	vector<string> date;
	vector<string> timeTracker;
	string taskTitle = "";
	string taskLocation ="";
	string taskPerson="";
	string taskDescription="";
	bool noTaskExist = true;
	bool taskTitleChecker = false;
	bool taskDescriptionChecker = false;
	bool taskLocationChecker = false;
	bool taskPersonChecker = false;
	bool taskStartTimeChecker = false;
	bool taskEndTimeChecker = false;
	bool taskPriorityChecker = false;
	bool repetitionChecker=false;
	struct  tm taskStartTime = {0};
	struct  tm taskEndTime = {0};
	struct tm currTime = {0};
	localtime_s(&currTime,&currentTime);
	localtime_s(&taskStartTime,&currentTime);
	localtime_s(&taskEndTime,&currentTime);
	taskStartTime.tm_sec=CommonConstant::DEFAULT_TIME_SECONDS;
	taskStartTime.tm_hour = CommonConstant::DEFAULT_TIME_HOURS;
	taskStartTime.tm_min = CommonConstant::DEFAULT_TIME_MINUTES;
	taskEndTime.tm_hour = CommonConstant::DEFAULT_END_TIME_HOURS;
	taskEndTime.tm_min = CommonConstant::DEFAULT_END_TIME_MINUTES;
	taskEndTime.tm_sec=CommonConstant::DEFAULT_END_TIME_SECONDS;
	int taskPriority= CommonConstant::TASK_INT_PRIORITY_MEDIUM;
	int taskFinished= CommonConstant::TASK_INT_STATUS_NOT_FINISHED;
	string sentenceToBeChecked;
	string taskFeature;
	int i = taskInputSentence.size()-1;

	while(!taskInputSentence.empty() || (i<0)){	
		if(i<0)
			break;
		sentenceToBeChecked = taskInputSentence[i];
		if(comparisonString(sentenceToBeChecked)){
			if(i+1==taskInputSentence.size()){
				taskInputSentence.pop_back();
				i--;
				continue;
			}
			noTaskExist = false;
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_TITLE){
				if(taskTitleChecker){
					repetitionChecker = true;
				}
				updateTaskFeature(taskTitle,taskInputSentence,i);
				taskTitleChecker = true;
			}
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_DESCRIPTION){
				if(taskDescriptionChecker){
					repetitionChecker = true;
				}
				updateTaskFeature(taskDescription,taskInputSentence,i);
				taskDescriptionChecker = true;
			}
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_PERSON){
				if(taskPersonChecker){
					repetitionChecker = true;
				}
				updateTaskFeature(taskPerson, taskInputSentence,i);
				taskPersonChecker = true;
			}
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_LOCATION){
				if(taskLocationChecker){
					repetitionChecker = true;
				}
				updateTaskFeature(taskLocation,taskInputSentence,i);
				taskLocationChecker = true;
			}
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_PRIORITY){
				if(taskPriorityChecker){
					repetitionChecker = true;
				}
				updateTaskPriority(taskPriority,taskInputSentence, i);
				taskPriorityChecker = true;
			}
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_START_TIME){
				if(taskStartTimeChecker){
					repetitionChecker = true;
				}
				updateTaskTime(taskStartTime, taskInputSentence,timeTracker,sentenceToBeChecked,i);
				taskStartTimeChecker = true;
			}
			if ((sentenceToBeChecked == CommonConstant::KEYWORD_TASK_END_TIME)){
				if(taskEndTimeChecker){
					repetitionChecker = true;
				}
				updateTaskTime(taskEndTime, taskInputSentence, timeTracker,sentenceToBeChecked, i);
				taskEndTimeChecker = true;
			}
			if (sentenceToBeChecked == CommonConstant::KEYWORD_TASK_DATE){
				dateChecker = true;
				updateTaskDate(date, taskInputSentence,i);
			}
			if(repetitionChecker)
				throw MESSAGE_KEY_REPETITION;
		}
		i--;
	}

	if(dateChecker){
		handleDate(date,timeTracker,taskStartTime,taskEndTime);
	}

	if(taskTitle == ""){
		taskTitle = CommonConstant::TASK_TITLE_UNTITLED;
	}

	if((taskStartTimeChecker)){
		attributesAssignmentStartTimeChange(taskStartTime,taskEndTime,currTime,taskEndTimeChecker,dateChecker);
		createTimedTask(taskTitle,taskDescription,taskLocation,taskPerson,taskPriority,taskStartTime,taskEndTime);
	} else if(taskEndTimeChecker){
		attributesAssignmentEndTimeChange(taskEndTime,currTime,taskEndTimeChecker,dateChecker);
		createDeadlineTask(taskTitle,taskDescription,taskLocation,taskPerson,taskPriority,taskEndTime);
	}
	else if (dateChecker){
		if((checkInvalidTimeSingle(taskStartTime))&&(checkInvalidTimeSingle(taskEndTime))){
			throw MESSAGE_INVALID_DATE;
		}
		createTimedTask(taskTitle,taskDescription,taskLocation,taskPerson,taskPriority,taskStartTime,taskEndTime);
	}
	else {
		if (noTaskExist){
			throw MESSAGE_INVALID_INPUT_FORMAT;
		}
		createFloatingTask(taskTitle,taskDescription,taskLocation,taskPerson,taskPriority);
	}
}


// This method takes in a string and compares whether it matches any of the keywords
bool CreateCommand::comparisonString(string sentenceToBeChecked)
{
	bool result = false;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_TITLE))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_DESCRIPTION))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_LOCATION))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_PERSON))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_PRIORITY))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_START_TIME))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_END_TIME))
		result = true;
	if(checkKeyWord(sentenceToBeChecked,CommonConstant::KEYWORD_TASK_DATE))
		result = true;
	return result;
}


//========================HIGHER LEVEL FUNCTIONS=====================
// These methods update a particular attribute of a task.

// updates task name, task description, task location, task person depending on the input.
void CreateCommand::updateTaskFeature(string& taskFeature, vector<string>& task, int index)
{
	int taskSize = task.size();
	for (int i = index+1; i < taskSize; i++){
		taskFeature = taskFeature + " " + task[i];
	}
	taskFeature = taskFeature.substr(1,taskFeature.size()-1);
	eraseTask(task,index);
}

//updates the task priority
void CreateCommand::updateTaskPriority(int& taskPriority, vector<string>& task, int index)
{
	int length = task.size();
	if(length>index+2){
		throw MESSAGE_INVALID_INPUT_FORMAT;
	}
	string tempTaskFeature = task[index+1];
	if((tempTaskFeature == CommonConstant::TASK_STRING_NUM_PRIORITY_HIGH)||(convertStringtoUpper(tempTaskFeature)==CommonConstant::TASK_STRING_PRIORITY_HIGH)){
		taskPriority = CommonConstant::TASK_INT_PRIORITY_HIGH;
	}
	else if((tempTaskFeature == CommonConstant::TASK_STRING_NUM_PRIORITY_MEDIUM)||(convertStringtoUpper(tempTaskFeature)==CommonConstant::TASK_STRING_PRIORITY_MEDIUM)){
		taskPriority = CommonConstant::TASK_INT_PRIORITY_MEDIUM;
	}
	else if((tempTaskFeature == CommonConstant::TASK_STRING_NUM_PRIORITY_LOW)||(convertStringtoUpper(tempTaskFeature)==CommonConstant::TASK_STRING_PRIORITY_LOW)){
		taskPriority = CommonConstant::TASK_INT_PRIORITY_LOW;
	}
	else{
		throw MESSAGE_INVALID_PRIORITY;
	}
	eraseTask(task,index);
}

// updates the task time.
void CreateCommand::updateTaskTime(struct tm& tempTime, vector<string>& task, vector<string>& tasktime, string key, int index)
{
	int hour=CommonConstant::DEFAULT_TIME_HOURS;
	int min=CommonConstant::DEFAULT_TIME_MINUTES;
	int tempIndex = index;
	tasktime.push_back(key);
	string tempSentence = task[index+1];
	int taskSize = task.size();
	if((index+2!=taskSize)){
		for (int i = index+2; i < taskSize; i++){
			tempSentence = tempSentence + " " + task[i];
		}
		tempSentence = convertStringtoLower(tempSentence);
	}
	eraseTask(task,tempIndex);
	selectTimeFormat(tempSentence,hour,min);
	tempTime.tm_hour = hour;
	tempTime.tm_min = min;
}

// copies date from the input vector into a temporary date vector
void CreateCommand::updateTaskDate(vector<string>& date, vector<string>& task,int index)
{
	int taskSize = task.size();
	for (int i = index; i < taskSize; i++){
		date.push_back(task[i]);
	}
	eraseTask(task,index);
}

//======================== SUPPORTING FUNCTIONS FOR THE WHOLE CLASS=====================
// These methods are called by different methods in the class depending on the requirement

// clears the space at the end if any
void CreateCommand::clearSpaceAtEnd(string& sentence)
{
	while(sentence.compare(sentence.size()-1,1," ") == 0){
		sentence.pop_back();
	}
}

string CreateCommand::convertStringtoLower(string sentence)
{
	transform(sentence.begin(), sentence.end(), sentence.begin(), ::tolower);
	return sentence;
}

string CreateCommand::convertStringtoUpper(string sentence)
{
	transform(sentence.begin(), sentence.end(), sentence.begin(), ::toupper);
	return sentence;
}

// eras the task from the index point till the end.
void CreateCommand::eraseTask(vector<string>& task, int index)
{
	task.erase(task.begin( )+index,task.begin( )+task.size()-1);
	task.pop_back();
}

void CreateCommand::resetTime(){
	time(&currentTime);
}

//======================== SUPPORTING FUNCTION FOR COMPARISON STRING=====================

bool CreateCommand::checkKeyWord(string inputSentence, string keyWord)
{
	std::size_t found;
	found = inputSentence.find(keyWord);
	if (found!=std::string::npos){
		if((inputSentence!=CommonConstant::KEYWORD_TASK_DESCRIPTION)){
			if(keyWord.size()!=inputSentence.size()){
				throw MESSAGE_KEYWORD_INVALID;
			}
		}
		return true;
	}
	return false;
}

//======================== CREATE TASK FUNCTIONS=====================

// creates a timed task with the entered attributes
void CreateCommand::createTimedTask(string taskTitle,string taskDescription,string taskLocation, string taskPerson, int taskPriority, struct tm startTime,struct tm endTime)
{
	TimedTask* newTask = new TimedTask();
	newTask->setDescription(taskDescription);
	newTask->setTitle(taskTitle);
	newTask->setLocation(taskLocation);
	newTask->setPerson(taskPerson);
	newTask->setPriority(taskPriority);
	newTask->setStartTimeAndDate(startTime);
	newTask->setEndTimeAndDate(endTime);
	task = newTask;
}

// creates a task with a deadline and with the attributes
void CreateCommand::createDeadlineTask(string taskTitle,string taskDescription,string taskLocation, string taskPerson, int taskPriority, struct tm endTime)
{
	DeadlineTask* newTask = new DeadlineTask();
	newTask->setDescription(taskDescription);
	newTask->setTitle(taskTitle);
	newTask->setLocation(taskLocation);
	newTask->setPerson(taskPerson);
	newTask->setPriority(taskPriority);
	newTask->setTimeAndDate(endTime);
	task = newTask;
}

// creates a floating task
void CreateCommand::createFloatingTask(string taskTitle,string taskDescription,string taskLocation, string taskPerson, int taskPriority)
{
	FloatingTask* newTask = new FloatingTask();
	newTask->setDescription(taskDescription);
	newTask->setTitle(taskTitle);
	newTask->setLocation(taskLocation);
	newTask->setPerson(taskPerson);
	newTask->setPriority(taskPriority);
	task = newTask;
}

//======================== SUPPORTING FUNCTIONS FOR CREATE TASK FUNCTIONS =====================

//this methods checks if the /et has been entered and if not assigns a an end time one hour after start time.
// It also checks if date has been assigned and if not gives it a tomorrow's date if the starting time is before 
// current time or the today's date of the starting time is after current time.
void CreateCommand::attributesAssignmentStartTimeChange(struct tm& startTime, struct tm& endTime,struct tm currTime, bool taskEndTimeChecker, bool dateChecker)
{
	if(!taskEndTimeChecker){
		endTime.tm_hour = startTime.tm_hour+CommonConstant::DATE_ADD_HOUR;
		endTime.tm_min = startTime.tm_min;
	}
	if(!dateChecker){
		if((startTime.tm_hour<currTime.tm_hour)||((startTime.tm_hour==currTime.tm_hour)&&(startTime.tm_min<=currTime.tm_min))){
			startTime.tm_mday+=CommonConstant::DATE_ADD_DAY;
			endTime.tm_mday+=CommonConstant::DATE_ADD_DAY;
			if(checkInvalidTimeCompare(startTime,endTime)){
				endTime.tm_mday = startTime.tm_mday + CommonConstant::DATE_ADD_DAY;
			}
		}
		else if ((endTime.tm_hour<currTime.tm_hour)||((endTime.tm_hour==endTime.tm_hour)&&(endTime.tm_min<=currTime.tm_min))){
			endTime.tm_mday+=CommonConstant::DATE_ADD_DAY;
		}
	}
	if(checkInvalidTimeSingle(startTime)){
		throw MESSAGE_INVALID_TIME_2;
	}
	if(checkInvalidTimeCompare(startTime,endTime)){
		throw MESSAGE_INVALID_STARTTIME_BEFORE_ENDTIME;
	}
}


// This methods checks if date has been assigned and if not gives it a tomorrow's date if the ending time is before 
// current time or the today's date of the ending time is after current time.
void CreateCommand::attributesAssignmentEndTimeChange(struct tm& endTime,struct tm currTime, bool taskEndTimeChecker, bool dateChecker)
{
	if(!dateChecker){
		if((endTime.tm_hour<currTime.tm_hour)||((endTime.tm_hour==currTime.tm_hour)&&(endTime.tm_min<currTime.tm_min))){
			endTime.tm_mday+=CommonConstant::DATE_ADD_DAY;
		}
	}
	if(checkInvalidTimeSingle(endTime)){
		throw MESSAGE_INVALID_TIME_2;
	}
}

//======================== SUPPORTING FUNCTIONS FOR UPDATING DATE =====================

//this methods matches the date vector and the timetracker vector to match /st and /et wit their respective dates. If only
// one date is entered, it assigns the date to both start time and end time.
void CreateCommand::handleDate(vector<string>& date, vector<string>& timeTracker,struct tm& startTime, struct tm& endTime)
{
	int dateCounter = 0;
	int tempSave;
	int size = date.size();
	for (int i = 0; i < size; i++){
		if(date[i]== CommonConstant::KEYWORD_TASK_DATE){
			dateCounter++;
		}
	}
	vector<string> tmpDate;
	if(dateCounter==2){
		if(timeTracker.size()==2){
			for (int i = 1; i >= 0; i--){
				if((timeTracker[i] == CommonConstant::KEYWORD_TASK_START_TIME)){
					extractDate(tmpDate,date,startTime,tempSave);
					eraseTask(date,tempSave);
				}
				if(timeTracker[i] == CommonConstant::KEYWORD_TASK_END_TIME){
					extractDate(tmpDate,date,endTime,tempSave);
					eraseTask(date,tempSave);
				}
			}
		}
		else if(timeTracker.size()==0){
			int dateSize = date.size();
			for (int i = dateSize-1; i >=0; i--){
				if(date[i] == CommonConstant::KEYWORD_TASK_DATE){
					tmpDate.clear();
					tempSave = i;
					for (int k = i+1; k < dateSize; k++){
						tmpDate.push_back(date[k]);
					}
					if(dateCounter==2){
						assignDateToTime(startTime,tmpDate);
						dateCounter--;
					}
					if(dateCounter==1){
						assignDateToTime(endTime,tmpDate);
						dateCounter--;

					}
				}
			}
		}
		else{
			throw MESSAGE_TWO_DATES_ENTERED;
		}
	}
	else if(dateCounter==1){
		date.erase(date.begin());
		assignDateToTime(startTime,date);
		assignDateToTime(endTime,date);
	}
	else{
		throw (MESSAGE_INVALID_DATE);
	}
}

//extracts date from the date vector to assign to a tm structure
void CreateCommand::extractDate(vector<string>& tmpDate, vector<string> date, struct tm& taskTime,int& tempDelete)
{
	tmpDate.clear();
	int dateSize = date.size();
	for (int j = dateSize-1; j >= 0; j--){
		if(date[j]== CommonConstant::KEYWORD_TASK_DATE){
			tempDelete = j;
			for (int k = j+1; k < dateSize; k++){
				tmpDate.push_back(date[k]);
			}
			break;
		}
	}
	assignDateToTime(taskTime,tmpDate);
}

/// checks which pattern of the date is followed and extracts the date to assign to a tm structure.
void CreateCommand::assignDateToTime(struct tm& taskTime, vector<string> date)
{
	int size = date.size();
	bool yearChecker = false;
	bool alternateFormatChecker = true;
	bool singleDate = false;
	bool singleMonth = false;
	bool notDatePatternMatcher = false;
	regex regdate("^((\\d{1})|(\\d{2}))/((\\d{1})|(\\d{2}))/(\\d{4})$");
	smatch match;
	if(std::regex_search(date[0], match, regdate)) {
		if(date.size()>1){
			throw MESSAGE_INVALID_DATE;
		}
		dateAssignmentNumbersFormat(match, date[0],taskTime.tm_mday,taskTime.tm_mon,taskTime.tm_year);
		notDatePatternMatcher = false;
	}
	else {
		dateAssignmentMonthWordFormat(date,taskTime,singleDate,singleMonth,yearChecker);
		if(!((singleMonth)&&(singleDate))){
			notDatePatternMatcher = true;
			alternateFormatChecker = false;
		}

	}
	assert((taskTime.tm_mday<CommonConstant::DATE_DAYS_MONTH_LIMIT)||(taskTime.tm_mday>0));
	assert((taskTime.tm_mon<CommonConstant::DATE_MAX_MONTH_YEAR)||(taskTime.tm_mon>=0));
	assert((taskTime.tm_year>CommonConstant::BASE_YEAR)||(taskTime.tm_year>0));
	if(notDatePatternMatcher){
		dateAssignmentDayFormat(date,taskTime,alternateFormatChecker);
	}
	if((checkInvalidDate(taskTime.tm_mday,taskTime.tm_mon+CommonConstant::DATE_ADD_DAY,taskTime.tm_year+CommonConstant::BASE_YEAR))||(!alternateFormatChecker)){
		throw MESSAGE_INVALID_DATE;
	}
	
	
}

// checks if the date entered exists
bool CreateCommand::checkInvalidDate(int day, int month, int year)
{
	bool result = false;
	if((day>CommonConstant::DATE_MAX_DAY_MONTH)||(month>CommonConstant::DATE_MAX_MONTH_YEAR)){
		result = true;
	}
	if ((day==CommonConstant::DATE_MAX_DAY_MONTH) && ((month == CommonConstant::MONTH_FEB)||(month == CommonConstant::MONTH_JUN)||(month == CommonConstant::MONTH_SEP)||(month == CommonConstant::MONTH_NOV))){
		result = true;
	}
	if ((day>=CommonConstant::DATE_THIRTY)&&(month==CommonConstant::MONTH_FEB)){
		result= true;
	}
	if ((month == CommonConstant::MONTH_FEB)&&(day==CommonConstant::DATE_FEBRUARY_LEAP_YEAR_DAYS)&&(!((year%CommonConstant::DATE_LEAP_YEAR == 0)&&((year%CommonConstant::DATE_CENTURY != 0)||(year%CommonConstant::DATE_LEAP_YEAR_CENTURY == 0))))){
		result= true;
	}
	return result;
}

//assigns date if the date was entered in dd/mm/yyyy format
void CreateCommand::dateAssignmentNumbersFormat(smatch match, string date,int& day,int& month, int& year)
{
	day = stoi(match[1]);
	month = stoi(match[4])-CommonConstant::DATE_SUBTRACT_DAY;
	year = stoi(match[7])- CommonConstant::BASE_YEAR;
}

// assigns date if the date entered contains the month as a word.
void CreateCommand::dateAssignmentMonthWordFormat(vector<string>& date, struct tm& tempTime, bool& singleDate, bool& singleMonth, bool& yearChecker)
{

	string monthLongForm[12] = {"january","february","march","april","may","june","july","august","september","october","november","december"};
	string monthShortForm[12] = {"jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"};

	regex regDay("(^((\\d{1})|(\\d{2}))$)");
	regex regYear("(^(\\d{4})$)");
	smatch match;
	int sizeDate = date.size();
	int result;
	for (int index = 0; index < sizeDate; index++){
		stringstream str(date[index]);
		str >> result;
		if(!str){
			for (int i = 0; i < CommonConstant::DATE_MAX_MONTH_YEAR; i++){
				if((convertStringtoLower(date[index])==monthShortForm[i])||(convertStringtoLower(date[index])==monthLongForm[i])){
					tempTime.tm_mon = i;
					singleMonth = !singleMonth;
				}
			}
		}
		else if(std::regex_search(date[index], match, regDay)){
			tempTime.tm_mday = stoi(match[1]);
			singleDate = !singleDate;
		}
		else if(std::regex_search(date[index], match, regYear)){
			tempTime.tm_year=stoi(match[0])-CommonConstant::BASE_YEAR;
			yearChecker = true;
		}
		else{
			throw MESSAGE_INVALID_DATE;
		}
	}
	if(((date.size()>2)&&(!yearChecker))||((date.size()>3)&&(yearChecker))){
		throw MESSAGE_INVALID_DATE;
	}
	if(!yearChecker){
		struct  tm currTime;
		localtime_s(&currTime,&currentTime);
		currTime.tm_sec=CommonConstant::DEFAULT_TIME_SECONDS;
		if((tempTime.tm_mon<currTime.tm_mon)||((tempTime.tm_mon==currTime.tm_mon)&&(tempTime.tm_mday<currTime.tm_mday))){
			tempTime.tm_year =  currTime.tm_year+1;
		}
		else{ 
			tempTime.tm_year =  currTime.tm_year;
		}
	}
	assert((tempTime.tm_mday<CommonConstant::DATE_DAYS_MONTH_LIMIT)||(tempTime.tm_mday>0));
	assert((tempTime.tm_mon<CommonConstant::DATE_MAX_MONTH_YEAR)||(tempTime.tm_mon>=0));
	assert((tempTime.tm_year>CommonConstant::BASE_YEAR)||(tempTime.tm_year>0));
}

// this methods assigns a date if the date entered contains a day or keywords like today and tomorrow.
void CreateCommand::dateAssignmentDayFormat(vector<string>& date, struct tm& tempTime, bool& alternateFormatChecker)
{
	string daysLongForm[7] = {"sunday","monday","tuesday","wednesday","thursday","friday","saturday"};
	string daysShortForm[7] = {"sun","mon","tue","wed","thu","fri","sat"};
	string individualKeyWords[3] = {"today","tomorrow","day after tomorrow"};
	if(date.size()>2){
		string dateSentence = date[0]+" "+date[1]+" "+date[2];
		if(dateSentence==individualKeyWords[2]){
			tempTime.tm_mday=tempTime.tm_mday+2;
			alternateFormatChecker = true;
		}
		else{
			throw MESSAGE_INVALID_DATE;
		}

	}
	int secondToBeAdded = 0;
	if(date.size()==2){
		if((date[0]== CommonConstant::DATE_NEXT)&&(date.size()==2)){
			secondToBeAdded = CommonConstant::MAX_SECONDS_DAY*CommonConstant::DATE_MAX_DAY_WEEK;
		}
		else{
			throw MESSAGE_INVALID_DATE;
		}
	}

	for (int i = 0; i < 2; i++){
		if(date[0]==individualKeyWords[i]){
			alternateFormatChecker = true;
			tempTime.tm_mday = tempTime.tm_mday + i;
			break;
		}
	}
	int index;
	if(date.size()==1){
		index = 0;
	}
	else if (date.size()==2){
		index = 1;
	}

	for (int i = 0; i < CommonConstant::DATE_MAX_DAY_WEEK; i++){
		if(date[index]==daysShortForm[i]){
			alternateFormatChecker = true;
			secondToBeAdded += (CommonConstant::DATE_MAX_DAY_WEEK-tempTime.tm_wday+i)*CommonConstant::MAX_SECONDS_DAY;
			break;
		}
		if(date[index]==daysLongForm[i]){
			alternateFormatChecker = true;
			secondToBeAdded += (CommonConstant::DATE_MAX_DAY_WEEK-tempTime.tm_wday+i)*CommonConstant::MAX_SECONDS_DAY;
			break;
		}
	}
	tempTime.tm_sec = tempTime.tm_sec + secondToBeAdded;
	time_t testTime;
	testTime = mktime(&tempTime);
	localtime_s(&tempTime,&testTime);
}

//======================== SUPPORTING FUNCTIONS FOR UPDATING TIME =====================

// Based on the user input, this method detects the user input for time and updates the time accordingly
void CreateCommand::selectTimeFormat(string tempSentence,int& hour,int& min)
{
	regex regtime12Format1("(^(\\d{1})|(\\d{2})):(\\d{2})((( am)|( pm)|(am)|(pm)|(AM)|(PM)|(Am)|(Pm)|(p)|(a))$)");
	regex regtime24Format1("(^((\\d{1})|(\\d{2}))(\\d{2})$)");
	smatch matchTime;
	if(std::regex_search(tempSentence, matchTime, regtime12Format1)) {
		if(tempSentence.size()>8){
			throw MESSAGE_INVALID_TIME;
		}
		hour = stoi(matchTime[1]);
		min = stoi(matchTime[4]);
		handleTimeTwelveHourFormat(matchTime[5],hour);

	}
	else if((std::regex_search(tempSentence, matchTime, regtime24Format1))){
		hour = stoi(matchTime[2]);
		min = stoi(matchTime[5]);
	}
	else{
		throw MESSAGE_INVALID_TIME_FORMAT;
	}
	assert((hour<=CommonConstant::MAX_HOUR)||(hour>=CommonConstant::MIN_HOUR));
	assert((hour<=CommonConstant::MAX_MINUTE)||(hour>=CommonConstant::MIN_MINUTE));
	if((hour>CommonConstant::DEFAULT_END_TIME_HOURS)||(min>CommonConstant::DEFAULT_END_TIME_MINUTES)){
		throw MESSAGE_INVALID_TIME;
	}
}

// this methods is used if the user input time with am or pm updates the time by adding 12 for pm.
void CreateCommand::handleTimeTwelveHourFormat(string timeConvention, int& hour)
{
	if(timeConvention.substr(0,1)==" "){
		timeConvention.erase(0,1);
	}
	if(((timeConvention == CommonConstant::TIME_AM)||(timeConvention == CommonConstant::TIME_A))&&(hour==CommonConstant::AM_PM_CONVERTION)){
		hour-=CommonConstant::AM_PM_CONVERTION;
	}
	if(((timeConvention == CommonConstant::TIME_PM)||(timeConvention == CommonConstant::TIME_P))&&(hour!=CommonConstant::AM_PM_CONVERTION)){
		hour +=CommonConstant::AM_PM_CONVERTION;
	}
}

// takes struct tm as a parameter and compares it with current time. 
bool CreateCommand::checkInvalidTimeSingle(struct tm taskTime)
{
	bool result = false;
	time_t tempTime;
	tempTime = mktime(&taskTime);
	if(tempTime<=currentTime){
		result = true;
	}
	return result;

}

// takes startime and endtime and ensures that endtime is after starttime.
bool CreateCommand::checkInvalidTimeCompare(struct tm startTime, struct  tm endTime)
{
	bool result = false;
	time_t startTimeDiffFormat;
	startTimeDiffFormat = mktime(&startTime);
	time_t endTimeDiffFormat;
	endTimeDiffFormat = mktime(&endTime);
	if(endTimeDiffFormat<=startTimeDiffFormat){
		result = true;
	}
	return result;
};
